﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.WindowsCE.Forms;
using System.Runtime.InteropServices;

namespace DeviceCF
{
    /// <summary>
    /// Summary description for RasManager. Changed from Roland Beuker's code
    /// </summary>
    public class RasManager : MessageWindow
    {
        private const int RASCS_PAUSED = 0x1000;
        private const int RASCS_DONE = 0x2000;
        private const int WM_RASDIALEVENT = 0xCCCD;

        private RASDIALPARAMS rasDialParams;
        private int rasSession;

        //the Singleton Implement
        // Static members are lazily initialized.
        // .NET guarantees thread safety for static initialization 
        public static readonly RasManager Instance = new RasManager();

        public delegate void NotifyEvent();
        public event NotifyEvent OnConnectedEvent;
        public event NotifyEvent OnDisconnectedEvent;

        private class RASDIALPARAMS : AdvancedMarshaler
        {
            private const int RAS_MaxEntryName = 20;
            private const int RAS_MaxPhoneNumber = 128;
            private const int RAS_MaxCallbackNumber = 48;
            private const int UNLEN = 256;
            private const int PWLEN = 256;
            private const int DNLEN = 15;
            private const int MAX_PATH = 260;
            private const int RAS_MaxDeviceType = 16;
            public int dwSize = 0;

            [CustomMarshalAs(SizeConst = RAS_MaxEntryName + 1)]
            public string szEntryName = null;

            [CustomMarshalAs(SizeConst = RAS_MaxPhoneNumber + 1)]
            public string szPhoneNumber = null;

            [CustomMarshalAs(SizeConst = RAS_MaxCallbackNumber + 1)]
            public string szCallbackNumber = null;

            [CustomMarshalAs(SizeConst = UNLEN + 1)]
            public string szUserName = null;

            [CustomMarshalAs(SizeConst = PWLEN + 1)]
            public string szPassword = null;

            [CustomMarshalAs(SizeConst = DNLEN + 1)]
            public string szDomain = null;

            public byte dwSubEntry = 0;
            public byte dwCallbackId = 0;

            public RASDIALPARAMS()
            {
                dwSize = (int)GetSize();
                data = new byte[dwSize];
                Serialize();
            }
        }

        private class RASCONNSTATUS : AdvancedMarshaler
        {
            private const int RAS_MaxDeviceType = 16;
            private const int RAS_MaxDeviceName = 128;
            public uint dwSize = 0;
            public int rasconnstate = 0;
            public uint dwError = 0;
            [CustomMarshalAs(SizeConst = RAS_MaxDeviceType + 1)]
            public string szDeviceType = null;
            [CustomMarshalAs(SizeConst = RAS_MaxDeviceName + 1)]
            public string szDeviceName = null;

            public RASCONNSTATUS()
            {
                dwSize = (uint)GetSize();
                data = new byte[dwSize];
                Serialize();
            }
        }

        public enum RASCONNSTATE : int
        {
            RASCS_OpenPort = 0, //0
            RASCS_PortOpened, //1
            RASCS_ConnectDevice, //2
            RASCS_DeviceConnected, //3
            RASCS_AllDevicesConnected, //4
            RASCS_Authenticate, //5
            RASCS_AuthNotify, //6
            RASCS_AuthRetry, //6
            RASCS_AuthCallback, //8
            RASCS_AuthChangePassword, //9
            RASCS_AuthProject, //10
            RASCS_AuthLinkSpeed, //11
            RASCS_AuthAck, //12
            RASCS_ReAuthenticate, //13
            RASCS_Authenticated, //14
            RASCS_PrepareForCallback, //15
            RASCS_WaitForModemReset, //16
            RASCS_WaitForCallback, //17
            RASCS_Projected, //18
            RASCS_Interactive = RASCS_PAUSED, //4096
            RASCS_RetryAuthentication, //4097
            RASCS_CallbackSetByCaller, //4098
            RASCS_PasswordExpired, //4099
            RASCS_Connected = RASCS_DONE, //8192
            RASCS_Disconnected //8193
        } ;


        [DllImport("coredll.dll", CharSet = CharSet.Auto)]
        private static extern Int32 RasGetEntryDialParams(string lpszPhoneBook,
        byte[] lpRasDialParams,
        out UInt32 lpfPassword);

        [DllImport("coredll.dll", CharSet = CharSet.Auto)]
        private static extern int RasDial(IntPtr pDialExtensions,
        string pPhonebook,
        byte[] pRasDialParam,
        uint pNotifierType,
        IntPtr pHwnd,
        ref int pRasConn);

        [DllImport("coredll.dll", CharSet = CharSet.Auto)]
        private static extern int RasHangUp(int pSession);

        [DllImport("coredll.dll", CharSet = CharSet.Auto)]
        private static extern uint RasGetConnectStatus(int pSession, byte[] lpRasConnStatus);

        
        private RasManager()
        {
            rasSession = 0;
            rasDialParams = new RASDIALPARAMS();
        }

        public void DialUp(string pEntryName, string pUserName, string pPassword)
        {
            //check the connection state first
            if (0 != rasSession)
            {
                RASCONNSTATE resConnState = (RASCONNSTATE)GetStatus();
                if (RASCONNSTATE.RASCS_Connected == resConnState ||
                    RASCONNSTATE.RASCS_PortOpened == resConnState ||
                    RASCONNSTATE.RASCS_DeviceConnected == resConnState ||
                    RASCONNSTATE.RASCS_AllDevicesConnected == resConnState ||
                    RASCONNSTATE.RASCS_Authenticate == resConnState ||
                    RASCONNSTATE.RASCS_AuthAck == resConnState ||
                    RASCONNSTATE.RASCS_Authenticated == resConnState)
                {
                    return;
                }
            }

            rasDialParams.szEntryName = pEntryName;
            rasDialParams.Serialize();
            UInt32 lpfPassword = 0;
            int lResult = RasGetEntryDialParams(null, rasDialParams.ByteArray, out lpfPassword);
            if (lResult != 0)
            {
                throw new ApplicationException("RasGetEntryDialParams(null,RasDialParams.ByteArray, ref " + lpfPassword + ") failed with error code: " + lResult);
            }

            rasDialParams.Deserialize();
            rasDialParams.szUserName = pUserName;
            rasDialParams.szPassword = pPassword;
            rasDialParams.Serialize();
            lResult = RasDial(IntPtr.Zero, null, rasDialParams.ByteArray, 0xFFFFFFFF, this.Hwnd, ref rasSession);

            if (lResult != 0)
            {
                throw new ApplicationException("RasDial(IntPtr.Zero, null, RasDialParams.ByteArray, 0xFFFFFFFF, this.Hwnd, ref " + rasSession + ") failed with error code: " + lResult);
            }
        }

        public int GetStatus()
        {
            //if the session is blank, it means it is disconnected
            if (0 == rasSession)
            {
                return (int)RASCONNSTATE.RASCS_Disconnected;
            }

            RASCONNSTATUS lRasStatus = new RASCONNSTATUS();
            uint lReturn = RasGetConnectStatus(rasSession, lRasStatus.ByteArray);
            lRasStatus.Deserialize();
            if (lReturn == 0)
            {
                return lRasStatus.rasconnstate;
            }
            else
            {
                throw new ApplicationException("RasGetConnectStatus(" + rasSession + ",lRasStatus.ByteArray) failed with error code: " + lReturn);
            }
        }

        public void HangUp()
        {
            if (rasSession != 0)
            {
                int lStatus = RasHangUp(rasSession);
                if (lStatus != 0)
                {
                    throw new ApplicationException("RasHangUp(" + rasSession + ") failed with error code: " + lStatus);
                }
                rasSession = 0;
            }
            else
            {
                OnDisconnected();
            }
        }

        private void OnConnected()
        {
            if (OnConnectedEvent != null)
            {
                OnConnectedEvent();
            }
        }

        private void OnDisconnected()
        {
            if (OnDisconnectedEvent != null)
            {
                OnDisconnectedEvent();
            }
        }

        protected override void WndProc(ref Message pMsg)
        {
            if (pMsg.Msg == WM_RASDIALEVENT)
            {
                switch ((int)pMsg.WParam)
                {
                    case (int)RASCONNSTATE.RASCS_OpenPort:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_PortOpened:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_ConnectDevice:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_DeviceConnected:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_AllDevicesConnected:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_Authenticate:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_AuthNotify:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_AuthRetry:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_AuthCallback:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_AuthChangePassword:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_AuthProject:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_AuthLinkSpeed:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_AuthAck:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_ReAuthenticate:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_Authenticated:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_PrepareForCallback:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_WaitForModemReset:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_WaitForCallback:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_Projected:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_Interactive:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_RetryAuthentication:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_CallbackSetByCaller:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_PasswordExpired:
                        {
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_Connected:
                        {
                            OnConnected();
                            break;
                        }
                    case (int)RASCONNSTATE.RASCS_Disconnected:
                        {
                            OnDisconnected();
                            break;
                        }
                    default:
                        {
                            break;
                        }
                }
                //Console.WriteLine(((RASCONNSTATE)pMsg.WParam).ToString());
            }
            base.WndProc(ref pMsg);
        }
    }
}
